from sympy import *

from fractions import  Fraction 
from rizefs import perfect_prime

from sympy.abc import x

__all__ = ["tau", 
        ]
tau = 2 * pi

class TwoPoles(object):
    fac_rev = [0x124,0x249,0x3b5]
    four_p2 = [0xd,0x49]
    
    x16d = 0x16d
    x1165 = 0x1165
    
    fox_dog = 'the quick brown fox jumps over the lazy dog'
    
    fun231 = lambda i: i // 2 if i % 2 == 0 else i*3 +1
    
    t6 = int(tau*(10**6))
    e9 = int(E*(10**9))
    xa2 = 0xa2
    polaron = 0x1e6a2
    fac_polaron = polaron // xa2
    days = x16d * x1165
    x3a8_90 = (0x3a8,0x90)
    
    def reversal_num(self,n):
        def fun0(i):
            k = reversal(i)
            return k+i if k!=i else i
        _,r0 = next(cycle_length(fun0,n))
        return r0
    
    def reversal_num2(self,n2):
        k = 0
        n = 196
        num0 = reversal(n)
        while num0 != n and n <= n2:
            n = num0 + n
            num0 = reversal(n)
            k += 1
        return k
    
    def special_add(self,num0):
        return sum([int(i) for i in list(str(num0))])
    
    def special_num(self,n):
        k = []
        while n > 10:
            n = self.special_add(n)
            k.append(n)
        return k
    
    def shrink(self,n):
        str0 = str(n)
        num0 = max([int(i) for i in list(str0)]) + 1
        return int(str0,num0)
    
    def shrink_num(self,n):
        _,r0 = next(cycle_length(self.shrink,n))
        return r0
    
    def get_prime2(self):
        lst = []
        for i in perfect_prime[:10]:
            for j in primerange(i,100):
                n = i*j
                if reversal(n) == n:
                    lst.append([i,j])
        return lst
    
    def tip(self):
        print("%d = %d * %d" % (self.days,self.x16d,self.x1165))
        print("1 year = 365 day")
        print("%d = %d * %d + x" % (self.x1165,self.x16d,12))
        print("1 year = 12 month")

    def _substep(self,lst0):
        len0 = len(lst0)
        assert(len0 % 2 == 0)
        lst = []
        len2 = len0 // 2
        for i in range(len2):
            lst.extend([lst0[i],lst0[-i-1]])
        return lst
    
    def get_substep(self,s0):
        return ''.join(self._substep(list(s0)))
    
    def get_days(self):
        n49_2 = 0x49 ** 2
        t0 = divisors(0o5155040)
        str0 = self.get_substep('123456')
        for i in range(1,10):
            n3 = int('%s%d' %(str0,i))
            i0 = 48*n3+n49_2
            if is_sqrt(i0) and sqrt(i0) % 24 ==1 and solve(12*x**2 + 0x49*x-n3)[1] in t0:
                return n3

    def sum_fac(self,n):
        return sum(divisors(n))-n

    def get_389(self):
        sum_fac0 = self.sum_fac(0o5155040)
        return (igcd(sum_fac0,0o5155040),igcd(divisor_count(0o5155040),0o5155040))

    def get_fac_rev(self):
        lst = []
        for i in divisors(0o5155040):
            if i>10 and reversal(i) == i:
                lst.append(i)
        return lst

class Part0(object):
    x22e09 = 0x22e09
    part =[1,9/8, 5/4, 4/3, 3/2, 5/3, 15/8,2]
    part4 = [24*i for i in part]
    fpart = [1, Fraction(9, 8), Fraction(5, 4), Fraction(4, 3), Fraction(3, 2), Fraction(5, 3), Fraction(15, 8), 2]
    fpart2 = [24*i for i in fpart]
    m012 = [210, 252, 189, 315, 280, 224, 0, 168, 102]
    dfpart =[Fraction(5,4), Fraction(3,2), Fraction(9,8), Fraction(15,8), Fraction(5,3), Fraction(4,3),
            0, # gen 2:5 no no not no
            1,
            Fraction(17, 28)      # 18,72
            ]
    part_degree = [105,101,96,93,3,11,24,45]
    part_totient = [6,20,40,12,24,84,252,120]
 
    def __init__(self):
        lst = []
        for i in self.part_degree:
            i0 = i*tau/0x168
            i2 = (cos(i0)+sin(i0))*sqrt(2)
            i4 = Fraction(float(i2)).limit_denominator(8)
            lst.append(i4)
        self.fpart_degree = lst
        self.part_sqrt = [1, 
                3/sqrt(7),
                -1 + 6/sqrt(7),
                3/sqrt(5),
                4/sqrt(7),
                -1 + 6/sqrt(5),
                5/sqrt(7),
                2]
        lst = []
        for i in self.part_totient:
            j = sum(divisors(i))-i
            i4 = Fraction(j,i).limit_denominator(8)
            lst.append(i4)
        self.fpart_totient = lst
        self.tower = pow(2,1/12)
        self.towers = [Fraction(self.tower**i).limit_denominator(8) for i in range(1,12)]
        fpart3 = self.fpart.copy()
        fpart3.append(x)
        self.fpart3 = fpart3
        self.m12 = Matrix(3,3,self.m012)
        self.m3 = Matrix(3,3,self.fpart3)
        assert( limit(4/(2*x - 1)-4/(2*x + 2), x, 1) == 3)

    def get_4bg9(self):
        t0 = TwoPoles()
        str0 = '%d%d%d' % (ri,lm,fs)
        n9 = 999999
        for i in range(10):
            str0 = t0.get_substep(str0)
            n3 = int(str0)
            if igcd(n3,n9) == n3 :
                return n3

    def get_part(self,a,b):
        t = b - a
        ls1 = []
        for i in self.fpart:
            t1 = (a + t * (i-1))
            assert t1.denominator  == 1
            ls1.append(t1.numerator)
        return ls1

    def s_even24(self):
        '''
        mt 15:36 loaves 
        mt 15:37 basketfuls 
        '''
        x, y = symbols('x y')
        abs_even = [ Fraction(i, 7) for i in range(1,8)]
        a1 = abs_even[0]
        a = Matrix([
            # re
            solve((abs_even[0]-x)/a1 - x),
            # mi
            solve((abs_even[1]-x)/a1 - x),
            # fa
            solve((abs_even[2]-x)/a1 + x - 1),
            # sol
            solve((abs_even[3]-x)/a1 - x ),
            # la
            solve((abs_even[4]-x)/a1 + x - 1),
            # si
            solve((x - abs_even[5])/a1 + x - 1),
            ])
        b = [0]
        b.extend(a.values())
        b.extend([1])
        return [i+1 for i in b ]

    def filter(self, lst):
        assert len(lst) == 8
        return [lst[1],lst[2],lst[6]]

    def get_farey(self,n):
        lst = []
        len0 = int(3*n**2/pi**2)
        for i in range(2,n+1):
            for j in range(1,i):
                if igcd(j,i) ==1:
                    c = Fraction(j,i)
                    lst.append(c)
        assert (len(lst)>len0)
        lst.sort()
        return lst

_p0 = Part0()
__all__.extend(["TwoPoles", "Part0"])

class Fish4(object):
    a0 = 0x78
    a8 = 0x80
    x9956e72d9b1 = 0x9956e72d9b1
    f50 = [4,6,8,12,20]
    x32 = 0x32
    x18 = 0x18
    a = 0xa
    d = 0xd
    fish4 = [
    '.....407......', 
    '...37........1',
    '...37.0.......',
    '1.53..........',
    ]
    hh = 0x275
    ab = 0x173
    bb = 0x197
    aa = 0x172
    x5c9 = 0x5c9
    x133 = 0x133 
    x3b5 = 0x3b5
    x3b5_ret = [
            [513, # 153 
                570, 855],
            [470, # 407 
                332, 30],
            173  # 371
            ]
    fish4_tip = [
            [[5, 6], 'electric, half strong'],
            [[3, 8], 'magnetic'],
            [[3, 1, 7], ' 3 + 1 gravity'],
            [[1, 10], ' half  weak'],
            ]

    find_dict = {
            153:11,
            370:12,
            371:27,
            407:103,
            }
    x10001 = [17,257,0x10001]

    def findstr(self,s0,s2):
        for i in s2:
            i0 = s0.find(i)
            if i0 != -1:
                s0 = s0[i0+1:]
            else:
                return False
        return True

    def get317(self,s0):
        lst =[]
        for i in s0.split('.'):
            if not i == '':
                lst.append(i)
        for i in lst:
            s0 =s0.replace(i,' ')
        lst =[]
        for i in s0.split():
            lst.append(len(i))
        return lst

    def get_third(self,t0):
        lst = []
        a3, _ = integer_nthroot(t0,2)
        a3 += 1
        for i in range(a3):
            for j in range(i+1,a3):
                if i**2 + j**2 == t0:
                    lst.append([i,j])
        for i,j in lst:
            gs1 = (j**2-i**2,2*i*j,t0)
        return gs1
    
    def get_area(self,a,b,c):
        s = (a + b + c)
        return sqrt(s*(s-2*a)*(s-2*b)*(s-2*c)) / 4

    def get_5(self):
        se3 = self.d
        se2 = self.get_third(se3)
        a,b,c = se2
        return [se2,self.get_area(a,b,c)]

    def get_37_79(self):
        lst = []
        for i in primerange(1,100):
            for j in primerange(i,100):
                d1,d2 = 0x1e*i-0xd*j, 0xd*i-0x1e*j
                a1,a2 = abs(d1), abs(d2)
                num0, n2 = join_two(a1,a2), join_two(a2,a1)
                if all([isprime(i) for i in [a1,a2 ,num0,n2]]):
                    lst.append((i,j))
        return lst

    def is_fish4(self,num0):
        ''' john 21:11 '''
        k = 3
        str0 = str(num0)
        n2 = 0
        for i in str0:
            n2 += int(i)**k
        return num0 == n2

    def inverse(self,a,m):
        from sympy.core.numbers import igcdex
        inv = igcdex(a,m)[0]
        return inv+m if inv<0 else inv
    
    def relprime(self,n):
        k = 2
        while igcd(k,n) != 1:
            k = nextprime(k)
        return k
    
    def transform(self,n):
        tn = totient(n)
        rp0 = self.relprime(tn)
        d = self.inverse(rp0,tn)
        k0 = int(n/48)
        fpart0 = _p0.get_part(24*k0,48*k0)
        fpart0 = _p0.filter(fpart0)
        set_f = set(fpart0)
        lst = []
        for i in fpart0:
            pirn0 = pow(i,rp0,n)
            lst.append(pirn0)
        lst2 = [int(i) for i in lst]
        set_l = set(lst)
        sis = set_f.intersection(set_l)
        lst3 = []
        for j in lst2:
            n0 = str(j) if j in sis else j
            lst3.append(n0)
        return [fpart0, lst3,d]

class AngDev(object):
    x47 = 0x47           # 407
    angst = 0x7296af   #76
    assert( hex(371) == '0x173')
    reduce = 0x51763     #  153 and 76
    angel = int('angel',36)
    devil = int('devil',36)
    adam = int('adam',36)
    answer = 0xf4629    #42 
    fac71 = [ 0x37c7f, 0x2e4b979, 0xcb06149]
    fac101 = [0x6c279f03a0f, 0x4bbe4964e1a8b11]
    fac479 = [
            0x1fd6b7f,
 0x1771e51e9,
 0x1bdfe11625cd601ddb46e7585e6ec6ad6f5140c64417a009,
 0x193291907cf490d867410f3d0e8d5a17a08a65a67375c435eb2d049bb51,
 ]
    
    def isprime_lst(self,lst):
        for i in lst:
            assert( isprime(i))
    
    def pop_num(self,n0):
        set_p = set(primefactors(n0))
        set_pp = set(perfect_prime)
        t2 = set_p.difference(set_pp)
        if len(t2) == 1:
            t0 = t2.pop()
        else:
            t0 = t2
        return t0
    
    def __init__(self):
        assert (primefactors(2**self.x47 - 1) == self.fac71)
        assert (self.pop_num(self.angel) == self.x47)
        assert (self.pop_num(self.devil) == self.angst)
        assert (self.pop_num(self.answer) == self.reduce)
        assert(self.pop_num(self.adam) == {101, 479})
        assert(Mul(*self.fac101) == 2**101-1)
        assert(Mul(*self.fac479) == 2**479-1)
        for i in [self.fac101,self.fac479]:
            self.isprime_lst(i)

__all__.extend(["Fish4", "AngDev"])

class Velocity(object):
    m0 = Matrix(3,3,[4,9,2,3,5,7,8,1,6])
    m1 = Matrix(3,3,[4,9,2,3,8,7,8,1,6])
    m2 = Matrix(3,3,[8,9,2,4,5,7,8,1,6])
    m3 = Matrix(3,3,[7,9,2,3,5,7,8,1,3])
    m4 = Matrix(3,3,[4,9,2,3,-4,7,8,1,6])
    v0 = int(m0.det())
    v10 =     Fraction(int(m1.det()), v0)
    v20 =     Fraction(int(m2.det()), v0)
    v30 =     Fraction(int(m3.det()), v0)
    v_part = [v30,v20,v10,v30,v20,v30,v10]
    
    fdpart = {
            1: Fraction(9, 8),
     Fraction(9, 8): Fraction(5, 4),
     Fraction(5, 4): Fraction(4, 3),
     Fraction(4, 3): Fraction(3, 2),
     Fraction(3, 2): Fraction(5, 3),
     Fraction(5, 3): Fraction(15, 8),
     Fraction(15, 8): 2,
     }
    x49 = 0x49
    def __init__(self):
        v_part1 = []
        for k in sorted(self.fdpart.keys()):
            v = self.fdpart[k]
            v_part1.append(v / k)
        assert(v_part1 == self.v_part )
        assert(self.v10 == min(self.v_part))

class HideSeek(object):
    # 149
    x491 = 0x491
    p149 = [149,419,491,941]
    square2 =[24, # do
        36, # sol
        ]
    square21 =[1,
        27, # re
        ]
    square3 =[ 24, 58 , 105, ]

    square_duality_not = [334,835]
    a10 = 0x750
    x2da = 0x2da
    o300 = 0o300

    def get_149_double(self):
        lst = []
        for i in self.p149:
            for j in self.p149:
                k = join_two(i,j)
                if isprime(k):
                    lst.append(k)
        return lst 
    
    def len_ninth(self,n0):
        s0 = str(n0)
        lst = []
        for j in ninth:
            s2 = str(j)
            if s2 in s0:
                lst.append(s2)
        return len(lst) 

__all__.extend(["Velocity", "HideSeek"])

class Ask0(object):
    '''
0:())                 
8:(
 *** 
_____
)
16:(
  *
_____
_____
_____
)
10:(
_____
_____
)
8:(
 *** 
_____
)
16 is angel num,
8 is half angel,
thus,
half half angel[4] -> half adam[5] -> half angel[8] -> adam[10] -> angel[16] -> infinite[oo] ->whole-seeing-eye[0]
devil[13] = half angel[8] + half adam[5]
0o5155040 -> 1169 ( x**2 -1 ) -> (7,0xa7) ( x * a)
1+ perfect_perfect[6]  = 7
167 = 67 + 3 + 97
heaven :
    master: whole-seeing-eye
    control: infinite
    slave: angel
world :
    master: angel
    control: adam
    slave: half angel
hell :
    master: devil(half angel + half adam)
    control: half adam
    slave: half half angel 
    '''
    a90 = 0x6e910
    x3d9 = [(0x3d9, 28), (40920, 0x491)]
    a149 = [1,4,9]

    def test_asks(self):
        for i in dir(self):
            if i[:4] == 'ask_':
                assert (getattr(self,i)() == 0xa7)

    def ask_12(self):
        t0 = TwoPoles()
        tzolkin = 260
        earth = t0.x16d
        venus = 584
        round0 = lcm([tzolkin,earth,venus])
        for i in range(1,10):
            i0 = round0*i**2
            if is_sqrt(i0 +1):
                return sqrt(i0 + 1) // 7

    def get_perfect_perfect(self):
        pp2 = perfect_prime[:4]
        for i in pp2:
            p2 = 2**(i-1)*(2**i -1)
            fac0 = divisors(p2)[:-1]
            k = Mul(* fac0)
            if k == p2:
                assert (sum([i**3 for i in fac0]) == p2 ** 2)
                assert (self.two_equ_3(p2))
                return p2

    def two_equ_3(self,n):
        f0 = [divisor_count(i) for i in divisors(n)]
        f3 = [ i**3 for i in f0]
        return sum(f3) == sum(f0)**2

    def ask_7_89(self):
        lst = []
        n0 = Mul(*primerange(2,8))
        for i0 in primerange(3,n0):
            k0 = 0
            for j in range(n0):
                n2 = j*(j+1) + i0
                n3 = gcd(n2,n0)
                if n3 != 1:
                    k0 += 1
            if k0 == 0:
                lst.append(i0)
        assert( lst[4] == 0x89)
        return lst[5]

    def ask_7_49(self):
        n0 = int(E**7)
        return (n0 + 0x49 ) //7

    def count_6(self,a,b,c):
        from sympy.combinatorics.generators import symmetric
        k = [a,b,c]
        m = list(map(str,k))
        six = 0
        for j in symmetric(3):
            ls1 = j.list()
            s2 = ''.join([m[j] for j in ls1])
            str0 = int(s2)
            if str0 % 2 == 0:
                s2 = str0 // 2
            else :
                s2 = str0
            if isprime(s2):
                six += 1
        return six

    def ask_199(self):
        lst = []
        for i0 in primerange(2,300):
            k = sum([int(i)**2 for i in str(i0)])
            if k<i0:
                n0 = i0 - k
                if is_sqrt(n0):
                    lst.append(i0)
        return lst[1]

class Template(object):
    '''
    ud_quark = 
    electron = 
    positron = 
    uy_electron = 
    uy_ud_quark =
    '''
    x, y, z = symbols('x y z')
    
    # gen 1:5
    evening = int('evening',36)
    gravity_template = Matrix(3,3,[96, 108, 120, 128, 144, 160, 180, 192,x])
    gravity_template2d = Matrix(3,3,[96, 108, 120, 128, 144, 160, y, 192,x])
    gravity_template3d = Matrix(3,3,[96, 108, 120, 128, 144, 160, y, z,x])
    morning = int('morning',36)
    #
    m_river = [
            Matrix(3,3,[-229, 269, -139, -367, 431, -223, 4, 0, 0xd]),
            Matrix(3,3,[45,  12, 3, 5,7,  23, 20, 21,64]),
            ]
    m_r_det = [0,0,]
    x1473 = 0x1473
    x1463 =    [96, 108, 120, 128, 144, 160, 180, 192,0x1463]
    b88 = [1,19, 24, 27, 30, 32, 36, 40, 45, 48]
    x68 = 0x68
    
    def change_zero(self,n):
        n0 = n
        k = 1
        while n0 != 1:
            n0 = totient(n0)
            k *= -1
        return k + 1

_t0 = Template()

__all__.extend(["Ask0", "Template"])

class Perfect(object):
    f50 = [4,6,8,12,20]
    live_river = Matrix(2,2,[6, 143, 7, 0xa7])

    def lucas(self,p):
        s0 = 4
        t = 2**p -1
        n = p - 2
        for i in range(n):
            s0 = (s0 * s0 -2) % t
        return s0

    def check_465(self,num0,n2):
        n4 =(num0 + n2)  // 2
        if isprime(n4):
            return n4
    
    def get_465(self):
        limit0 =  100000
        i = 3
        while i < limit0:
            if isprime(i):
                for j in primerange(i+1, limit0):
                    n4 = self.check_465(i,j)
                    if n4 :
                        t1 = list(map(lambda x:str(x).zfill(5),[i,n4,j]))
                        tij = join_two(i,j)
                        ti4 = join_two(i,n4)
                        tj4 = join_two(j,n4)
                        t4i = join_two(n4,i)
                        if all([isprime(i) for i in [tij,ti4,tj4,t4i]]):
                            if t1[0][0] != '0':
                                return[int(i) for i in t1]
                        i = j - 2
                        break
            i += 2
    
    def  get_89(self,river):
        ''' 
        '''
        pp3 = perfect_prime[:10]
        lst = []
        for i in pp3:
            for j in pp3:
                k = 0
                lrs = river.solve(Matrix(2,1,[i,j])).values()
                for m in lrs:
                    if isprime(abs(m)):
                        k +=  1
                if k == 2:
                    lst0 = [i,j]
                    lst.append(lst0)
        return lst
    
    def get_pp(self,lst1):
        k1 = len(lst1)
        lst2 = []
        for i in range(1,k1+1):
            num0 = 0
            for j in range(k1-1,-1,-1):
                num0 += lst1[k1-j-1] * i**j
            lst2.append(num0)
        return lst2

class Zeta0(object):
    '''See Also
    get_z0s
    '''
    def __init__(self, ab,abc):
        x, y, z, t = symbols('x y z t')
        k, m, n = symbols('k m n', integer=True)
        f, g, h = symbols('f g h', cls=Function)
        x0,y0 = ab
        x1,y1 = abc
        so0 = solve([
            self.get_x_y(x0,y0),
            self.get_x_y(x1,y1),
            ])
        self.ab = so0[x],so0[y]
        assert isprime ( abs(self.ab[1]))
        self.root = [ab[1],abc[1]]
        y10 = y1-y0
        x10 = x1-x0
        k0 = int(x10 / y10)
        k2 = x1 - k0 * y1
        self.k20 = (k0,k2)
        root0 = []
        for i in self.root:
            y2 = i*k0+k2
            root0.append((y2,i))
        assert(root0 == [ab,abc])

    def get_x_y(self, x0,y0):
        x, y  = symbols('x y ')
        return x0**2 - y0**3-x*y0-y


class Mass(object):
    _x = symbols('x')
    def get_static_mass(self,t0):
        f0 = self.mass(t0)
        a,b,c = [f0.coeff(self._x, i) for i in reversed(range(3))]
        x0 = -b/(a*2)
        min_mass = expand(f0 - a*(self._x-x0)**2)
        return [x0,min_mass]
    
    def mass(self,mat1):
        m1 = {}
        mvs = mat1.values()
        assert (len(mvs) == 9)
        d = 3
        for x in range(d):
            for y in range(d):
                m1[(x,y)]=mvs[y*d+x]
        center = sum(mvs)
        mass = 0
        for x in range(d):
            m0 = 0
            m2 = 0
            for y in range(d):
                m0 += m1[(x,y)]
                m2 += m1[(y,x)]
            a1,a2 =d*m0-center,d*m2-center
            mass += a1*a1 +a2*a2
        return expand(mass)

__all__.extend(["Perfect", "Zeta0","Mass"])

ninth = [ 18, 27, 36, 45, 54, 63, 72, 81, ]
ri = ninth [0]
fs = ninth [3]
lm = ninth [6]

def reversal(n):
    str0 = str(n)
    return int(str0[::-1])

def int_c(str0, c3):
    k = 1
    n = 0
    for i in str0[::-1]:
        n += k * int(i)
        k *= c3
    return n

def get_name():
    n = 0x65 +  0x49   
    s0 = hex(n).replace('a','z')[2:]
    return 'ri' + s0 + 'fs'

def is_sqrt(num0):
    num0 = abs(num0)
    if num0 < 289:
        _ , t1 = integer_nthroot(num0,2)
        return t1
    else:
        for i1 in [7,0xd,17]:
            j1 = jacobi_symbol(num0,i1)
            if j1 == -1:
                return False
        _ , t1 = integer_nthroot(num0,2)
        return t1

def join_two(a,b):
    return int("%d%d" % (a,b))

def get_z0s(z1s):
    '''See Also
    ========
    Zeta0'''
    d1 = {}
    for i,z in z1s.items():
        d2 = {}
        a = z[0]
        b = z[1]
        c = [str(i) for i in range(8)]
        gp1 = _p0.get_part(a,b)
        for j in range(8):
            d2[c[j]] = gp1[j]
        d1[i] = d2
    return d1

__all__.extend(["ninth", "ri","fs","lm",
"reversal","int_c","get_name",
"is_sqrt","join_two","get_z0s"
])
